import React, { Component } from 'react'
import qs from 'querystring'
import { IRouteProps } from '../../router/routertypes'
import { StoreState } from '../../store/index'
import CONFIG from '../../config'
import { HOME } from '../../router/constants'
import {
  Switch,
  Route,
  Redirect,
  withRouter,
  RouteComponentProps,
} from 'react-router-dom'
import { connect } from 'react-redux'

// 交叉类型
//  ReturnType<T>是TS内置工具，用于获取函数 T 返回值的类型
type Props = IRouteProps &
  ReturnType<typeof mapStateToProps> &
  RouteComponentProps

// mapStateToProp：建立一个从（外部的）`state`对象到（UI 组件的）`props`对象的映射关系，即将store中的数据作为props绑定到组件上。接受state的UI/容器组件需要实现该函数。
// 返回一个（state）对象，对象内的键值对会传入到UI组件，在UI组件中通过 this.props.xxx或解构赋值获取
const mapStateToProps = (state: StoreState) => {
  return {
    isLogin: state.user.isLogin,
  }
}

const RouteView: React.FC<Props> = function ({
  childrenRoutes, // 路由对象属性
  isLogin, // 通过mapStateToProp映射到RouteView组件
  location, // withRouter传入的location
  redirect,
  ...props
}: Props) {
  // 取消不是当前页面的所有http请求
  if (Array.isArray(window.axiosCancelTokenStore)) {
    window.axiosCancelTokenStore.forEach(item => {
      if (item.pathname !== location.pathname) {
        item.cancel()
      }
    })
    window.axiosCancelTokenStore = []
  }
  const { meta } = props
  if (meta) {
    if (meta.title) {
      document.title = `${meta.title} - ${CONFIG.title}`
    } else {
      document.title = CONFIG.title
    }
  }
  // 是否已经登录
  // 立即执行函数
  // isLogin发生变化，该组件会重新执行这段程序，重新渲染
  // 当刷新页面时，redux的store会初始化，isLogin为false，为了防止页面刷新redux初始化，需要用到redux数据持久化的库：redux-persist
  const auth = (function () {
    if (meta?.requiresAuth) {
      if (isLogin) return true
      return false
    }
    return false
  })()
  // qs.parse()将URL解析成对象的形式,例如将 'projectId=85&appToken=abc' 解析成 {projectId:'85' , appToken:'abc' }
  // qs.stringify()将对象 序列化成URL的形式，以&进行拼接
  // 如果登录了，跳到登录页面时，则直接重定向回地址栏redirectUrl指向的地址或者后台首页
  // 例如我们手动改变地址栏，突然从后台的页面跳转到/login，会直接重定向或跳转到后台首页
  if (location.pathname === '/login' && isLogin) {
    const redirectUrl = qs.parse(location.search).redirect as string
    const url = redirectUrl || HOME.HOME_INDEX.path
    return <Redirect to={url} />
  }
  return (
    // 没有 path 属性的< Route > 或者 没有 from 属性的 < Redirect > 将总是匹配到当前的地址(location)，然后进行渲染。
    <Route
      render={routeProps => {
        return auth || props.path === '/login' || props.path === '*' ? (
          <props.component {...routeProps} {...props}>
            {childrenRoutes && childrenRoutes.length > 0 ? (
              <Switch>
                {childrenRoutes.map((route, index) => (
                  // 因为是RouteViewContainer，因此会自动传入isLogin
                  <RouteViewContainer {...route} key={index} />
                ))}
              </Switch>
            ) : null}
          </props.component>
        ) : (
          // 由于用户删除了缓存中的token，当跳转到其他后台页面时，会跳转到login重新登录
          // 且在地址栏带上之前想跳转的页面的地址，登录后再重新跳转回该页面
          <Redirect
            to={{
              pathname: '/login',
              search: `?redirectUrl=${location.pathname}`,
            }}
          />
        )
      }}
    ></Route>
  )
}

// `connect`用于从 UI 组件生成容器组件，接受两个参数：`mapStateToProps`和`mapDispatchToProps`。它们定义了 UI 组件的业务逻辑。前者负责输入逻辑，即将`state`映射到 UI 组件的参数（`props`），后者负责输出逻辑，即将用户对 UI 组件的操作映射成 Action。
// 把不是通过路由切换过来的组件中，用 withRouter 包裹，将react-router 的 history、location、match 三个对象传入props对象上
export const RouteViewContainer = connect(mapStateToProps)(
  withRouter(RouteView),
)
